package business.services;

import java.util.List;
import java.util.Vector;
import common.Contexto;
import data.entities.CalendarioClase;
import data.entities.Clase;
import data.entities.Socio;
import data.repository.IRepository;
import data.repository.QueryParameter;
import data.repository.Repository;
import data.repository.RepositoryException;
import business.interfaces.ICalendarioClase;
import business.interfaces.IClase;

public class ServicioClases implements IServicioClases {
	private IRepository<Clase> repositorioClases;
	private IRepository<CalendarioClase> repositorioCalendarios;
	
	private IServicioClientes servicioClientes;
	
	public ServicioClases(IServicioClientes servicioClientes) {
		this.repositorioClases = new Repository<Clase>();
		this.repositorioCalendarios = new Repository<CalendarioClase>();
		this.servicioClientes = servicioClientes;
	}
	
	public void generarCalendarioClases(Boolean sobreescribirCalendarioExistente) throws ServiceException {
		try {
			List<CalendarioClase> calendarioClases = this.repositorioCalendarios.getAll(CalendarioClase.class);
			
			if(!calendarioClases.isEmpty() && !sobreescribirCalendarioExistente) {
				throw new ServiceException("No se puede sobreescribir el calendario actual de clases");
			}
			
			for(Integer dia = 1; dia <= 7; dia++) {
				for(Integer hora = 1; hora <= 24; hora++) {
					Boolean estaHabilitado = dia < 7 && hora >= 9 && hora <= 22;
					CalendarioClase calendarioClase = new CalendarioClase();
					
					calendarioClase.setDia(dia);
					calendarioClase.setHora(hora);
					calendarioClase.setCupo(Contexto.cupoMaximoPorClase);
					calendarioClase.setEstaLibre(true);
					calendarioClase.setHabilitado(estaHabilitado);			
	
					this.repositorioCalendarios.saveOrUpdate(calendarioClase);
					
				}
			}	
		} catch (RepositoryException e) {
			throw new ServiceException(e);
		}
	}
	
	public List<ICalendarioClase> getClasesDisponibles() throws ServiceException {
		String queryExpression = "from CalendarioClase c where c.estaLibre = :estaLibre";
		QueryParameter parameter = new QueryParameter("estaLibre", true);
		List<CalendarioClase> calendarioClases = new Vector<CalendarioClase>();
		
		try {
			calendarioClases = this.repositorioCalendarios.getAll(queryExpression, parameter);
		} catch (RepositoryException e) {
			throw new ServiceException(e);
		}
		
		return this.getClasesDisponibles(calendarioClases);
	}
	
	public IClase getClase(String dni) throws ServiceException {
		try {
			return this.getClaseActivaExistente(dni);
		} catch (RepositoryException e) {
			throw new ServiceException(e);
		}
	}

	public void anularClase(String dni) throws ServiceException {
		try {
			Clase claseExistente = this.getClaseActivaExistente(dni);
	
			if(claseExistente == null) {
				String mensaje = String.format("El socio con dni %s no tiene una clase activa para anular", dni);
				
				throw new ServiceException(mensaje);
			}
			
			claseExistente.setActiva(false);
			
			this.repositorioClases.saveOrUpdate(claseExistente);
			
			CalendarioClase calendarioClase = (CalendarioClase) claseExistente.getCalendario();
			
			Integer cupoActual = calendarioClase.getCupo();
			
			if(cupoActual == 0) {
				calendarioClase.setEstaLibre(true);
			}
			
			calendarioClase.setCupo(cupoActual + 1);
			
			this.repositorioCalendarios.saveOrUpdate(calendarioClase);
		} catch (RepositoryException e) {
			throw new ServiceException(e);
		}
	}
	
	public void cargarClase(String dni, Integer dia, Integer hora, Integer cantidadPorMes) throws ServiceException {
		try {
			Clase claseExistente = this.getClaseActivaExistente(dni);

			if(claseExistente != null) {
				String mensaje = String.format("El socio con dni %s ya tiene una clase activa. No se puede cargar otra", dni);
				
				throw new ServiceException(mensaje);
			}
			
			List<ICalendarioClase> clasesDisponibles = this.getClasesDisponibles();
			CalendarioClase calendarioClase = null;

			for(ICalendarioClase claseDisponible : clasesDisponibles) {
				if(claseDisponible.getDia().equals(dia) && claseDisponible.getHora().equals(hora)) {
					calendarioClase = (CalendarioClase) claseDisponible;
					break;
				}
			}
			
			if(calendarioClase == null) {
				String mensaje = String.format("No hay disponibilidad para el dia %s a las %s", dia, hora);
				
				throw new ServiceException(mensaje);
			}
			
			Socio socioExistente = (Socio) this.servicioClientes.getSocio(dni);
			Clase newClase = new Clase();
			
			newClase.setSocio(socioExistente);
			newClase.setCalendario(calendarioClase);
			newClase.setCantidadPorMes(cantidadPorMes);
			newClase.setActiva(true);
			
			this.repositorioClases.saveOrUpdate(newClase);
			
			Integer cupoActual = calendarioClase.getCupo();
			
			calendarioClase.setCupo(cupoActual - 1);
			
			if(calendarioClase.getCupo() == 0) {
				calendarioClase.setEstaLibre(false);
			}
			
			this.repositorioCalendarios.saveOrUpdate(calendarioClase);
		} catch (RepositoryException e) {
			throw new ServiceException(e);
		}
	}
	
	private List<ICalendarioClase> getClasesDisponibles(List<CalendarioClase> calendarioClases) {
		List<ICalendarioClase> result = new Vector<ICalendarioClase>();
		
		for(CalendarioClase calendarioClase : calendarioClases) {
			result.add(calendarioClase);
		}
		
		return result;
	}
	
	private Clase getClaseActivaExistente(String dni) throws RepositoryException {
		String queryExpression = "from Clase c where c.socio.cliente.dni = :dni and c.activa = :activa";
		QueryParameter parameter1 = new QueryParameter("dni", dni);
		QueryParameter parameter2 = new QueryParameter("activa", true);
		Clase claseActiva = this.repositorioClases.get(queryExpression, parameter1, parameter2);
		
		return claseActiva;
	}
}
